<template>
  <div>
    <!--
      el-upload组件中看见的图片和地址，都是本地的，不涉及任何的上传
      list-type="picture-card" - 照片墙
      action - 图片的上传地址
      action - 必写属性 - 如果没有确定的上传地址可以写空 - # - 空连接
      on-preview - 点击文件列表中已上传的文件时的钩子函数 - 点击放大镜，预览的时候执行
      on-remove - 钩子函数
      http-request：覆盖默认上传行为，也就是让action失效
      如果action为空，必须指定http-request
      limit：最大允许上传个数
      on-change - 文件状态改变时的钩子，添加文件、上传成功和上传失败都会被调用
      file-list - 显示默认上传的图片
    -->
    <el-upload
      action="#"
      list-type="picture-card"
      :on-preview="handlePictureCardPreview"
      :on-remove="handleRemove"
      :http-request="handleUpload"
      :limit="imgLimit"
      :class="{disabled: overLimit}"
      :on-change="handleChange"
      :file-list="fileList"
      :before-upload="handleBeforeUpload"
    >
      <i class="el-icon-plus" />
    </el-upload>
    <!--
      弹框组件
      visible - 控制显示隐藏（布尔值）
    -->
    <el-dialog :visible.sync="dialogVisible" title="图片预览">
      <img width="100%" :src="dialogImageUrl" alt="">
    </el-dialog>
    <!-- 进度条组件 -->
    <el-progress
      v-if="showProgress"
      style="width: 170px"
      :percentage="percent"
    />
  </div>
</template>

<script>
import COS from 'cos-js-sdk-v5'
// 对于当前组件内而言，创建的是全局的COS对象
const cos = new COS({
  SecretId: 'AKIDIfuy96sUu5C7pKkz0UjkZAWeaIpBtYzD',
  SecretKey: 'AHCwWyDKFyUovoTu8AzZw50e4e1B6mDO'
})
export default {
  props: {
    imgLimit: {
      type: Number,
      default: 1
    },
    // 针对 v-model 语法糖的实现 :value
    value: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      dialogImageUrl: '',
      dialogVisible: false,
      // 当前已经完成上传或选中的文件列表
      fileList: this.value || [],
      // 控制进度条的显示隐藏
      showProgress: false,
      percent: 0
    }
  },
  computed: {
    overLimit() {
      return this.fileList.length >= this.imgLimit
    }
  },
  watch: {
    // 监听props中value的变化，将这个变化及时同步到data中的fileList中
    value(newVal) {
      this.fileList = [...newVal]
    }
  },
  methods: {
    // 移除文件对象触发的函数
    /**
     *
     * @param {被移除的文件} file
     * @param {移除文件对象之后的数组对象} fileList
     */
    handleRemove(file, fileList) {
      // 图片移除以后回调的钩子函数
      // file：被移除的文件对象
      // 要做什么？
      // 从 data 的 fileList 中移除 file 对象
      // 因为 overLimit 计算属性，用的时 data 中 fileList 的长度
      this.fileList = fileList
    },
    handlePictureCardPreview(file) {
      // file？文件对象 - 里面包含了name（文件的名字）、url（文件的本地地址）
      // 让弹出层的img显示 - img的src = 图片的本地地址
      this.dialogImageUrl = file.url
      // 显示弹框
      this.dialogVisible = true
    },
    // 上传文件之前的钩子函数 - 文件校验
    handleBeforeUpload(file) {
      // file 对象，包含的字段：uid、name、type：文件的类型个格式、size：文件的大小（单位：b）
      // 1kb = 1024b 1Mb = 1024kb
      const validTypes = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png']
      if (!validTypes.includes(file.type)) {
        this.$message.error('图片格式不正确')
        // 终止上传 - 返回false或Promise的reject对象
        return false
      }
      if (file.size > 2 * 1024 * 1024) {
        this.$message.error('图片尺寸太大，只能支持不超过2M的文件')
        return false
      }
    },
    // 延迟关闭进度条
    closeProgressDelay(ms = 1000) {
      setTimeout(() => (this.showProgress = false), ms)
    },
    handleUpload(params) {
      // 没有文件不做任何处理
      if (!params) return
      const options = {
        // 存储桶名称
        Bucket: 'gentle-1311452200', /* 填入您自己的存储桶，必须字段 */
        Region: 'ap-chengdu', /* 存储桶所在地域，例如ap-beijing，必须字段 */
        // 文件名称
        Key: params.file.name, /* 存储在桶里的对象键（例如1.jpg，a/b/test.txt），必须字段 */
        // 文件对象
        Body: params.file, /* 必须，上传文件对象，可以是input[type="file"]标签选择本地文件后得到的file对象 */
        // 进度条
        onProgress: (progressData) => {
          this.percent = parseInt(progressData.percent * 100)
        }
      }
      this.showProgress = true
      cos.putObject(options, (err, data) => {
        // err - 错误对象
        // data是一个对象，里面有-一个 Location ，就是图片上传至腾讯云，腾讯云返回的地址，但是没有http前缀
        // if (err) {
        //   this.$message.error(err.message)
        //   return
        // }
        const { statusCode, Location } = data
        // 文件上传错误的统一处理
        if (err || statusCode !== 200) {
          this.$message.error('上传出错！')
          // 关闭进度条
          this.closeProgressDelay()
          return
        }
        // 用cos返回的图片地址，替换fileList中原图片的本地地址
        const cosImgUrl = 'http://' + Location
        this.fileList = this.fileList.map(item => {
          if (item.uid === params.file.uid) {
            item.url = cosImgUrl
            // 补充一个属性 update
            // item里面原来是没有这个属性的，补充这个属性的目的，是打一个标记，表示这个图片上传完成（后面会用到）
            item.update = true
          }
          return item
        })
      })
      // 关闭进度条
      this.closeProgressDelay()
      // 上传成功之后再去修改
      // 针对 v-model 语法糖的实现 - @input
      this.$emit('input', [...this.fileList])
      // console.log(this.fileList)
    },
    // 文件上传触发的函数
    /**
     * 只要文件发生变化，就会回调 on-change 钩子函数
     * @param {刚才操作的文件对象} file
     * @param {已上传的文件对象 - 数组对象} fileList
     */
    handleChange(file, fileList) {
      this.fileList = [...fileList]
    }
  }
}
</script>

<style>
  .disabled .el-upload--picture-card {
    display: none;
  }
</style>
